WEB-971 [Playwright] Extract Layer-2 selector, route and behavior con…#3627
WEB-971 [Playwright] Extract Layer-2 selector, route and behavior con…#3627devvaansh wants to merge 1 commit into
Conversation
|
Note
|
| Layer / File(s) | Summary |
|---|---|
Configuration contracts (behavior, routes, selectors) playwright/config/behavior.ts, playwright/config/routes.ts, playwright/config/selectors.ts |
New BEHAVIOR flags, AppRoutes/ROUTES hash-based registry with parameterized builders, and typed selector maps for login, dashboard, create-client, client-view, and close-client. |
BasePage contract documentation playwright/pages/BasePage.ts |
Clarified JSDoc that Layer 2 selector/route/behavior contracts are consumed by subclasses and that subclasses set url from ROUTES. |
LoginPage configuration integration playwright/pages/login.page.ts |
LoginPage now uses ROUTES.login, LOGIN_SELECTORS for locator getters, replaces private locators with selectors-backed getters, and adds assertLoginButtonInitialState() using BEHAVIOR.loginButtonStartsDisabled. |
ClientViewPage configuration integration playwright/pages/client-view.page.ts |
ClientViewPage now uses ROUTES.clientView(clientId), CLIENT_VIEW_SELECTORS for locators, refactors closedDateValue() selection, and conditionally skips dismissOverlay() when BEHAVIOR.overlayDismissNeeded is false. |
CloseClientPage configuration integration playwright/pages/close-client.page.ts |
CloseClientPage now sets url via ROUTES.clientAction(clientId, 'Close') and uses CLOSE_CLIENT_SELECTORS for closure form fields and confirm/cancel button resolution. |
Authentication helper playwright/auth-helpers.ts |
New authenticateRole(role, page, browser) writes verified storageState: resolves credentials, performs login via LoginPage, copies configured auth key from sessionStorage to localStorage, persists storageState file, and verifies it by launching a new context. |
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
- openMF/web-app#3457: Implements a similar Playwright storageState generation flow, copying a credential from
sessionStorageintolocalStoragefor persisted auth state. - openMF/web-app#2912: Introduced initial Playwright page object scaffolding (LoginPage/BasePage) that this PR updates to consume shared config.
- openMF/web-app#3504: Adds initial ClientView/CloseClient page objects and close-client E2E spec; this PR refactors those pages to use shared ROUTES/SELECTORS/BEHAVIOR.
Suggested reviewers
- alberto-art3ch
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title accurately describes the main change: extracting Layer-2 selector, route, and behavior contracts for Playwright testing, which is the primary objective of this PR. |
| Docstring Coverage | ✅ Passed | Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@playwright/config/behavior.ts`:
- Around line 47-51: Tests define the auth storage key in
BEHAVIOR.authStorageKey but auth.setup.ts still hardcodes 'mifosXCredentials',
causing duplicate definitions; fix by importing BEHAVIOR (or the exported
constant that contains authStorageKey) into the auth.setup module and replace
the literal 'mifosXCredentials' with BEHAVIOR.authStorageKey so all consumers
use the single source of truth.
In `@playwright/config/routes.ts`:
- Line 43: The route builder clientAction currently interpolates the raw action
into the path which can break for reserved characters; update clientAction (the
arrow function named clientAction) to percent-encode the action segment using
encodeURIComponent(action) before interpolation (optionally also encode id with
encodeURIComponent(id)) so the generated URL path is safe for all characters.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2ec06212-b35a-4dfc-8842-c6394edd4a93
📒 Files selected for processing (7)
playwright/config/behavior.tsplaywright/config/routes.tsplaywright/config/selectors.tsplaywright/pages/BasePage.tsplaywright/pages/client-view.page.tsplaywright/pages/close-client.page.tsplaywright/pages/login.page.ts
| * Angular persists Fineract credentials in sessionStorage under this | ||
| * key. The auth-setup project copies localStorage -> sessionStorage | ||
| * on every test to survive page reloads. | ||
| */ | ||
| authStorageKey: 'mifosXCredentials', |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Eliminate duplicate auth-storage key definitions to avoid contract drift.
Line 51 introduces BEHAVIOR.authStorageKey, but playwright/auth.setup.ts still hardcodes 'mifosXCredentials' (Lines 32-41 in the provided snippet). If one side changes, auth bootstrap can silently break across all tests.
♻️ Proposed follow-up (outside this file) to consume the contract
+import { BEHAVIOR } from './config/behavior';
-const creds = sessionStorage.getItem('mifosXCredentials');
+const creds = sessionStorage.getItem(BEHAVIOR.authStorageKey);
-localStorage.setItem('mifosXCredentials', creds);
+localStorage.setItem(BEHAVIOR.authStorageKey, creds);
-throw new Error('CRITICAL: mifosXCredentials not found in sessionStorage. ' + 'Did the auth storage key change?');
+throw new Error(`CRITICAL: ${BEHAVIOR.authStorageKey} not found in sessionStorage. Did the auth storage key change?`);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@playwright/config/behavior.ts` around lines 47 - 51, Tests define the auth
storage key in BEHAVIOR.authStorageKey but auth.setup.ts still hardcodes
'mifosXCredentials', causing duplicate definitions; fix by importing BEHAVIOR
(or the exported constant that contains authStorageKey) into the auth.setup
module and replace the literal 'mifosXCredentials' with BEHAVIOR.authStorageKey
so all consumers use the single source of truth.
19f551d to
63b7f10
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
playwright/auth-helpers.ts (1)
73-74: ⚡ Quick winUse the route contract in verification navigation.
Line 73 hardcodes
'/#/'; that bypassesROUTESand weakens the config-only portability goal for Angular/React swaps.Proposed diff
import { BEHAVIOR } from './config/behavior'; +import { ROUTES } from './config/routes'; @@ - await verifyPage.goto('/#/'); - await expect(verifyPage).not.toHaveURL(/.*login.*/, { timeout: 30000 }); + await verifyPage.goto(ROUTES.home); + await expect(verifyPage).not.toHaveURL(new RegExp(`${ROUTES.login}`), { timeout: 30000 });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@playwright/auth-helpers.ts` around lines 73 - 74, The navigation call verifyPage.goto('/#/') is hardcoded; replace it with the canonical route from your ROUTES constant (e.g., verifyPage.goto(ROUTES.home) or the ROUTES key that represents the app root) and import ROUTES if not already imported, and update the URL-check to avoid hardcoded login pattern by referencing ROUTES.login (or the appropriate login route) in the expectation (e.g., not.toHaveURL(new RegExp(ROUTES.login))) so both navigation and assertions use the shared ROUTES configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@playwright/auth-helpers.ts`:
- Around line 71-75: The verifyContext created via browser.newContext (and
verifyPage) may leak if the assertion await
expect(verifyPage).not.toHaveURL(/.*login.*/...) throws; wrap the navigation +
assertion in a try/finally so verifyContext.close() always runs in the finally
block (and close verifyPage if created separately). Specifically, modify the
block around verifyContext/verifyPage and the expect call to ensure
verifyContext.close() is invoked in a finally clause so the context is always
cleaned up even on assertion failures.
---
Nitpick comments:
In `@playwright/auth-helpers.ts`:
- Around line 73-74: The navigation call verifyPage.goto('/#/') is hardcoded;
replace it with the canonical route from your ROUTES constant (e.g.,
verifyPage.goto(ROUTES.home) or the ROUTES key that represents the app root) and
import ROUTES if not already imported, and update the URL-check to avoid
hardcoded login pattern by referencing ROUTES.login (or the appropriate login
route) in the expectation (e.g., not.toHaveURL(new RegExp(ROUTES.login))) so
both navigation and assertions use the shared ROUTES configuration.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 365f866b-a61a-4ef6-b389-19864863b42f
📒 Files selected for processing (8)
playwright/auth-helpers.tsplaywright/config/behavior.tsplaywright/config/routes.tsplaywright/config/selectors.tsplaywright/pages/BasePage.tsplaywright/pages/client-view.page.tsplaywright/pages/close-client.page.tsplaywright/pages/login.page.ts
✅ Files skipped from review due to trivial changes (1)
- playwright/pages/BasePage.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- playwright/pages/client-view.page.ts
- playwright/pages/close-client.page.ts
- playwright/pages/login.page.ts
- playwright/config/behavior.ts
- playwright/config/selectors.ts
| const verifyContext = await browser.newContext({ storageState: role.storageStateFile }); | ||
| const verifyPage = await verifyContext.newPage(); | ||
| await verifyPage.goto('/#/'); | ||
| await expect(verifyPage).not.toHaveURL(/.*login.*/, { timeout: 30000 }); | ||
| await verifyContext.close(); |
There was a problem hiding this comment.
Ensure verifyContext is always closed.
If the assertion on Line 74 fails, Line 75 is skipped and the context leaks.
Proposed diff
const verifyContext = await browser.newContext({ storageState: role.storageStateFile });
- const verifyPage = await verifyContext.newPage();
- await verifyPage.goto('/#/');
- await expect(verifyPage).not.toHaveURL(/.*login.*/, { timeout: 30000 });
- await verifyContext.close();
+ try {
+ const verifyPage = await verifyContext.newPage();
+ await verifyPage.goto('/#/');
+ await expect(verifyPage).not.toHaveURL(/.*login.*/, { timeout: 30000 });
+ } finally {
+ await verifyContext.close();
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const verifyContext = await browser.newContext({ storageState: role.storageStateFile }); | |
| const verifyPage = await verifyContext.newPage(); | |
| await verifyPage.goto('/#/'); | |
| await expect(verifyPage).not.toHaveURL(/.*login.*/, { timeout: 30000 }); | |
| await verifyContext.close(); | |
| const verifyContext = await browser.newContext({ storageState: role.storageStateFile }); | |
| try { | |
| const verifyPage = await verifyContext.newPage(); | |
| await verifyPage.goto('/#/'); | |
| await expect(verifyPage).not.toHaveURL(/.*login.*/, { timeout: 30000 }); | |
| } finally { | |
| await verifyContext.close(); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@playwright/auth-helpers.ts` around lines 71 - 75, The verifyContext created
via browser.newContext (and verifyPage) may leak if the assertion await
expect(verifyPage).not.toHaveURL(/.*login.*/...) throws; wrap the navigation +
assertion in a try/finally so verifyContext.close() always runs in the finally
block (and close verifyPage if created separately). Specifically, modify the
block around verifyContext/verifyPage and the expect call to ensure
verifyContext.close() is invoked in a finally clause so the context is always
cleaned up even on assertion failures.
…tracts - Add playwright/config/selectors.ts: typed selector registry for login, client-view, close-client, create-client and dashboard page objects. Single source of truth — no selector string is hardcoded in page logic. - Add playwright/config/routes.ts: hash-routing URL registry (/#/login, /#/clients, etc.) so no page object or spec ever hardcodes a route. - Add playwright/config/behavior.ts: app-level behavioral flags consumed by specs (loginButtonStartsDisabled, usesHashRouting, authStorageKey) so assertions stay framework-agnostic. - Refactor BasePage, LoginPage, ClientViewPage, CloseClientPage to import from the above contracts. Zero raw selector strings remain in any page object file (verified: grep returns empty). This is the prerequisite for the React port (MXWAR-91): React page objects will implement the same selectors interface with framework-native locators, allowing specs to run unchanged against both apps. Part of GSoC 2026 Epic WEB-967.
63b7f10 to
e796748
Compare
|
Actionable comments posted: 0 |
Description
Introduces a framework-agnostic Layer-2 contract between Playwright specs and page objects. Before this change, selector strings, route paths, and app behavioral flags were hardcoded directly inside page object methods — making them fragile to refactor and impossible to share across frameworks.
Three new contract files centralise everything:
playwright/config/selectors.ts- typed selector registry for all current page objects (login, client-view, close-client, create-client, dashboard). One place to update when the DOM changes.playwright/config/routes.ts- hash-routing URL registry so no page object or spec ever hardcodes/#/loginor/#/clientsagain.playwright/config/behavior.ts- app-level behavioral flags (loginButtonStartsDisabled,usesHashRouting,authStorageKey) consumed by specs instead of embedded in assertions.BasePage,LoginPage,ClientViewPage, andCloseClientPagewere refactored to import from these contracts. Zero raw selector strings remain in any page object file.This is a prerequisite for the React port (MXWAR-91): React page objects will implement the same
selectors.tsinterface with React-native locators, allowing the same spec files to run against both apps without modification.Part of GSoC 2026 Epic WEB-967.
Related issues and discussion
https://mifosforge.jira.com/browse/WEB-971
Parent Epic: https://mifosforge.jira.com/browse/WEB-967
Screenshots, if any
No UI changes- this PR touches only the Playwright test infrastructure.
Summary by CodeRabbit